Skip to content

fix: prevent keypress memory leak with multiple Inquirerer instances#67

Merged
pyramation merged 1 commit intomainfrom
devin/1772489252-fix-inquirerer-keypress-leak
Mar 3, 2026
Merged

fix: prevent keypress memory leak with multiple Inquirerer instances#67
pyramation merged 1 commit intomainfrom
devin/1772489252-fix-inquirerer-keypress-leak

Conversation

@pyramation
Copy link
Contributor

Summary

Fixes two defensive issues in the Inquirerer lifecycle that contribute to a keypress bug when multiple instances share process.stdin (e.g., during pgpm init -w).

TerminalKeypress.pause() — Now resets raw mode (setRawMode(false)) when pausing. Previously, after a checkbox/autocomplete/list prompt called pause() on ENTER, raw mode remained enabled, potentially interfering with subsequent readline-based text prompts. resume() already sets raw mode to true, so this makes the pair symmetric. destroy() already did this — pause() was the odd one out.

Inquirerer.close() — Now nullifies this.rl and this.keypress after closing/destroying them. This:

  • Makes double-close safe (the second call is a no-op)
  • Lets prompt methods (text(), checkbox(), etc.) detect a closed instance via their existing if (!this.rl) guard and fall back to non-interactive behavior instead of calling methods on a dead readline

Companion PR: constructive-io/constructive has a separate PR that passes the existing prompter through to initModule()scaffoldTemplate() so a second Inquirerer is never created on process.stdin in the first place.

Review & Testing Checklist for Human

  • Verify pause() raw mode reset doesn't break prompt chaining: After a checkbox prompt, the next text prompt should work correctly. resume() re-enables raw mode, so keypress-based prompts following pause() should still function. Worth running pgpm init -w interactively to confirm.
  • Check double-close safety: Confirm that calling prompter.close() twice (or exit()close()) doesn't throw. Check that prompts after close() fall back to non-interactive defaults.
  • Test with existing consumers: If any downstream projects call pause()/resume() directly on keypress or check for closed state on Inquirerer, verify they still work.

Test Plan

  1. Run an interactive inquirerer workflow with multiple checkbox/text question types in sequence
  2. Test pgpm init -w specifically (once the companion PR lands) to verify no double-echo or input conflicts
  3. Verify all 137 existing inquirerer tests pass ✅ (they do)

Notes

  • All 137 Jest tests pass
  • pause() is called by checkbox/autocomplete/list prompts when ENTER is pressed, before resolving the promise
  • This fixes the symptom but the root cause (duplicate Inquirerer on stdin) is fixed in the companion PR

Link to Devin Session: https://app.devin.ai/sessions/178a5957a56e4141bd3fd4d0a5e4a508
Requested by: @pyramation

- TerminalKeypress.pause() now resets raw mode (setRawMode(false)) to prevent
  raw mode from leaking into subsequent text prompts after checkbox/autocomplete
- Inquirerer.close() now nullifies this.rl and this.keypress references so
  subsequent calls detect the closed state and double-close is safe
@devin-ai-integration
Copy link

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@pyramation pyramation merged commit 88ead6f into main Mar 3, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant